'''
"doc_scanner.py" 文件中的 DocScanner 类提供了文档扫描的核心功能，功能包括
    - 加载图片
    - 找到文档的角落
    - 裁剪图片

- 通过将文档扫描功能封装在一个独立的类中，我们可以将其作为一个模块，方便在其他项目中重用。
- 这种模块化的设计使得代码更加模块化、可扩展和可测试。
- 它可以独立于其他部分进行开发、测试和维护，而不会对其他组件产生影响。
- 此外，将文档扫描功能单独放在一个文件中，使得代码结构更清晰、易于理解和维护。
- 通过将不同的功能分割到不同的文件中，我们可以更好地组织代码，减少文件的复杂性，并促进团队合作开发。
'''
import cv2 as cv  # 导入 OpenCV 库，用于图片处理和计算机视觉任务
import numpy as np  # 导入 numpy 库，用于进行数值计算

class DocScanner:
    def __init__(self):
        pass

    def load_image(self, file_path):
        img = cv.imread(file_path)
        if img is None:
            raise ValueError(f"无法加载图像: {file_path}. 请检查文件路径和文件完整性。")

        img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        ret, binary_img = cv.threshold(img_gray, 127, 255, cv.THRESH_BINARY)
        contours, hierarchy = cv.findContours(binary_img, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)

        extreme_pnts = None
        max_area = 0
        for cnt in contours:
            area = cv.contourArea(cnt)
            if area > max_area and hierarchy[0][0][3] == -1:  # 确保是最大的轮廓且是顶层轮廓
                extreme_pnts = cnt
                max_area = area

        corners = self.order_points(extreme_pnts.reshape(-1, 2))
        return img, corners

    def order_points(self, pts):
        """对给定的四个点进行排序，返回排序后的点。"""
        rect = np.zeros((4, 2), dtype="float32")
        s = pts.sum(axis=1)
        rect[0] = pts[np.argmin(s)]
        rect[2] = pts[np.argmax(s)]
        diff = np.diff(pts, axis=1).flatten()
        rect[1] = pts[np.argmin(diff)]
        rect[3] = pts[np.argmax(diff)]

        return rect

    def crop_image(self, img, corners):
        """根据四个角落裁剪图片。"""
        top_left_corner = corners[0]
        top_right_corner = corners[1]
        bottom_right_corner = corners[2]
        bottom_left_corner = corners[3]

        # 计算目标图片的宽度和高度
        width, height = self.get_image_dimensions((top_left_corner, top_right_corner, bottom_right_corner, bottom_left_corner))

        # 根据以上宽度和高度创建于原有四个点顺序对应的目标四个点的坐标
        dst_points = np.float32([[0, 0], [width-1, 0], [width-1, height-1], [0, height-1]])

        # 通过投影变换展平文档
        matrix = cv.getPerspectiveTransform(np.float32(corners), dst_points)
        dst = cv.warpPerspective(img, matrix, (int(width), int(height)))
        return dst  # 返回展平后的图像

    def get_image_dimensions(self, corners):
        """计算图片的宽度和高度。"""
        x_values = [corner[0] for corner in corners]
        y_values = [corner[1] for corner in corners]
        width = int(max(x_values) - min(x_values))
        height = int(max(y_values) - min(y_values))
        return width, height
    